////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "SCLabData.hpp"

#include <wx/window.h>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <wx/gauge.h>
#include <wx/checkbox.h>
#include <wx/toolbar.h>
#include <wx/statusbr.h>
#include <wx/bmpbuttn.h>
#include <wx/stattext.h>

#include "ProgramPanel.hpp"
#include "ProgramStatePanel.hpp"
#include "ExecutionPanel.hpp"
#include "PlaybackPanel.hpp"
#include "UniversePanel.hpp"
#include "ConsolePanel.hpp"
#include "SystemsPanel.hpp"
#include "ExplorerPanel.hpp"
#include "VisualiserPanel.hpp"

#include "SComputationInfo.hpp"

#include <sstream>
#include <cstdio>

#include "IOException.hpp"
#include <exception>

#include "SCLabFrame.hpp"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace Exceptions;

SCLabData::SCLabData()
:	plugin_dirs(vector<string>(1,"plugins")), timer(0),
	parent(NULL), tool_bar(NULL), status_bar(NULL), program_panel(NULL)
{
	// Set memorised states
	this->engine.SetMaxNbMemorisedStates(1000);
}

void SCLabData::LoadProgram() {
	wxString path = wxFileSelector("Load binary file","bin","","bsc","SC Binary files (*.bsc)|*.bsc|All files (*.*)|*.*",wxOPEN,this->parent);
	if (!path.empty()) {
		try {
			// Read program
			this->program.ReadByteCode(string(path.c_str()));
			// Output command
			if (this->console_panel != NULL)
				this->console_panel->Print(_("Program '") + wxString(this->program.GetName().c_str()) + _("' successfully loaded\n"));
			// Initialise universe panel fields
			if (this->universe_panel != NULL) {
				this->universe_panel->Initialise(this->program.GetWordLength());
				this->universe_panel->SetEngine(&this->engine);
			}
			// Initialise visualiser panel fields
			if (this->visualiser_panel != NULL)
				this->visualiser_panel->SetEngine(&this->engine);
			// Reinitialisation
			this->ResetExecution();
			// Set some program panel fields
			if (this->program_panel != NULL)
				this->program_panel->Initialise(wxString(this->program.GetName().c_str()),this->engine.GetNbSystems());
		}
		catch(exception & e) {
			if (this->visualiser_panel != NULL) this->visualiser_panel->Clear();
			this->program.Clear();
			this->engine.Clear();
			wxMessageBox(_(e.what()));
		}
	}
}

void SCLabData::ResetExecution(const bool init_all) {
	// Clear engine data
	this->engine.Clear();
	// Clear memorised states
	this->memorised_lab_states.clear();
	// Reset last computation time
	this->last_ctime = 0;
	// Reload data from program
	this->engine.InitData(this->program,this->plugin_dirs);
	// Set status bar
	if (this->status_bar != NULL) {
		this->status_bar->SetStatusText(_("program: ") + wxString(this->program.GetName().c_str()), 0);
		this->status_bar->SetStatusText(_("systems: ") + wxString::Format("%d", this->engine.GetNbSystems()), 1);
		this->status_bar->SetStatusText(_("state: stopped"), 2);
		this->status_bar->SetStatusText(_("contexts: ") + wxString::Format("%d", this->engine.GetContexts().size()), 3);
		this->status_bar->SetStatusText(_("computations: 0"), 4);
	}
	// Reset some program state panel fields
	if (this->program_state_panel != NULL)
		this->program_state_panel->Initialise(this->engine.GetContexts().size(), this->engine.GetNbIterations(), this->engine.GetNbComputations());
	if (this->tool_bar != NULL) {
		this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RESET_TOOL, false);
		this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, false);
	}
	// Reset some execution panel fields
	if (this->execution_panel != NULL) {
		// Enable execution run button, one step forward and run to next computation
		this->execution_panel->run_togglebutton->Enable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RUN_TOOL, true);
		this->execution_panel->forward_button->Enable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_STEP_TOOL, true);
        this->execution_panel->next_computation_togglebutton->Enable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_NEXT_COMPUTATION_TOOL, true);
		// Enable record to filepicker
		this->execution_panel->record_to_filepicker->Enable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RECORD_TO_TOOL, true);
		// Reset record file name
		this->execution_panel->record_to_filepicker->SetPath(_(""));
	}
	// Close potential record stream
	if (this->rec_stream.is_open()) this->rec_stream.close();
	// Reset some playback panel fields
	if (this->playback_panel != NULL) {
		// Disable the play log buttons
		this->playback_panel->run_togglebutton->Disable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_RUN_TOOL, false);
		this->playback_panel->forward_button->Disable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_STEP_TOOL, false);
		// Enable play from filepicker
		this->playback_panel->play_from_filepicker->Enable();
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_LOAD_FROM_TOOL, true);
		// Reset playback file name
		this->playback_panel->play_from_filepicker->SetPath(_(""));
	}
	// Close potential log stream
	if (this->log_stream.is_open()) this->log_stream.close();
	// Reset universe panel fields
	if (this->universe_panel != NULL)
		this->universe_panel->RefreshUniverse(this->engine.Universe());
	// Reset systems panel fields
	if (this->systems_panel != NULL)
		this->systems_panel->SetData(this->program,this->engine.GetSystems());
	// Reset visualiser and reinitialise
	if (this->visualiser_panel != NULL) {
		this->visualiser_panel->Initialise(
			this->program,
			this->engine.GetSystems(),
			static_cast<unsigned int>(&this->engine.Universe()-&this->engine.GetSystems().front()),
			init_all
		);
		this->visualiser_panel->Refresh();
		if ((this->tool_bar != NULL) && (this->visualiser_panel->IsEnabled())) {
			this->tool_bar->EnableTool(ID_MASKS_LOAD_TOOL, true);
			this->tool_bar->EnableTool(ID_VISUALISER_CONTROL_MASKS_SNAPSHOT_TOOL, true);
			this->tool_bar->EnableTool(ID_VISUALISER_CONTROL_GRAPH_SNAPSHOT_TOOL, true);
			this->tool_bar->EnableTool(ID_VISUALISER_CONTROL_EXPLORER_SNAPSHOT_TOOL, true);
			this->tool_bar->EnableTool(ID_VISUALISER_CONTROL_STRUCTURE_SNAPSHOT_TOOL, true);
		}
	}
	// Output command
	if (this->console_panel != NULL)
		this->console_panel->Print(_("Program '") + wxString(this->program.GetName().c_str()) + _("' initialised\n"));
}

void SCLabData::SetRecordFile(const string & filename) {
	// Close a potential previous stream
	if (this->rec_stream.is_open()) this->rec_stream.close();
	// Clear flags
	this->rec_stream.clear();
	// Open new stream
	this->rec_stream.open(filename.c_str());
	// Check stream state and enable engine recording if ok
	if (this->rec_stream.is_open()) {
		// Output command
		if (this->console_panel != NULL)
			this->console_panel->Print(_("Record file '") + wxString(filename.c_str()) + _("' successfully opened for writing\n"));
	}
	else {
		wxMessageBox(_("Cannot open recording file: "+wxString(filename.c_str())));
		// Output command
		if (this->console_panel != NULL)
			this->console_panel->Print(_("Record file '") + wxString(filename.c_str()) + _("' opening failure\n"));
	}
}

void SCLabData::SetLogFile(const string & filename) {
	// Close a potential previous stream
	if (this->log_stream.is_open()) this->log_stream.close();
	// Clear flags
	this->log_stream.clear();
	// Open new stream
	this->log_stream.open(filename.c_str());
	// Check stream state and enable relevant buttons if ok
	if (this->log_stream.is_open()) {
		if (this->playback_panel != NULL) {
			this->playback_panel->run_togglebutton->Enable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_RUN_TOOL, true);
			this->playback_panel->forward_button->Enable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_STEP_TOOL, true);
		}
		// Output command
		if (this->console_panel != NULL)
			this->console_panel->Print(_("Log file '") + wxString(filename.c_str()) + _("' successfully opened for reading\n"));
	}
	else {
		wxMessageBox(_("Cannot open log file: "+wxString(filename.c_str())));
		// Output command
		if (this->console_panel != NULL)
			this->console_panel->Print(_("Log file '") + wxString(filename.c_str()) + _("' opening failure\n"));
	}
}

void SCLabData::NewIterationLoop(const bool run, const bool next_computation) {
	// Set status bar
	if (this->status_bar != NULL) this->status_bar->SetStatusText((run?_("state: running"):_("state: stopped")), 2);
	// Set tool bar
	if (this->tool_bar != NULL) {
		this->tool_bar->ToggleTool(ID_PROGRAM_EXECUTION_RUN_TOOL, run);
		this->tool_bar->ToggleTool(ID_PROGRAM_EXECUTION_NEXT_COMPUTATION_TOOL, run && next_computation);
		this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, !run && (this->engine.GetNbMemorisedStates()>0));
		this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RESET_TOOL, !run);
		this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_STEP_TOOL, !run);
	}
	// Enable or disable undo and reset
	if (this->program_state_panel != NULL) {
		this->program_state_panel->undo_button->Enable(!run && (this->engine.GetNbMemorisedStates()>0));
		this->program_state_panel->reset_button->Enable(!run);
	}
	// Enable or disable execution commands
	if (this->execution_panel != NULL) {
        this->execution_panel->forward_button->Enable(!run);
        this->execution_panel->run_togglebutton->SetValue(run);
        this->execution_panel->next_computation_togglebutton->SetValue(run && next_computation);
	}
	// Output command
	if (this->console_panel != NULL)
        if (!next_computation)
            this->console_panel->Print(_("Execution ") + wxString(run?"running":"paused") + wxString::Format(" (%d)\n", this->engine.GetNbComputations()));
        else
            this->console_panel->Print(_("Execution ") + wxString(run?"running to":"reached") + _(" next computation") + wxString::Format(" (%d)\n", this->engine.GetNbComputations()));
}

void SCLabData::PlaybackLoop(const bool run) {
	// Set status bar
	if (this->status_bar != NULL) this->status_bar->SetStatusText((run?_("state: playing"):_("state: stopped")), 2);
	// Enable or disable undo and reset
	if (this->program_state_panel != NULL) {
		this->program_state_panel->undo_button->Enable(!run && (this->engine.GetNbMemorisedStates()>0));
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, this->program_state_panel->undo_button->IsEnabled());
		this->program_state_panel->reset_button->Enable(!run);
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RESET_TOOL, this->program_state_panel->reset_button->IsEnabled());
	}
	// Enable or disable new execution run button
	if (this->execution_panel != NULL) {
		this->execution_panel->run_togglebutton->Enable(!run);
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RUN_TOOL, this->execution_panel->run_togglebutton->IsEnabled());
		this->execution_panel->forward_button->Enable(!run);
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_STEP_TOOL, this->execution_panel->forward_button->IsEnabled());
	}
	// Set playback_panel panel
	if (this->playback_panel != NULL) {
		if (run) this->playback_panel->forward_button->Disable();
		else {
			this->playback_panel->run_togglebutton->SetValue(false);
			if (this->tool_bar != NULL) this->tool_bar->ToggleTool(ID_PROGRAM_PLAYBACK_RUN_TOOL, false);
			this->playback_panel->run_togglebutton->Enable(this->log_stream.good());
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_RUN_TOOL, this->log_stream.good());
			this->playback_panel->forward_button->Enable(this->log_stream.good());
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_STEP_TOOL, this->log_stream.good());
		}
	}
	// Output command
	if (this->console_panel != NULL)
		this->console_panel->Print(_("Playback ") + wxString(run?"running":"paused") + wxString::Format(" (%d)\n", this->engine.GetNbComputations()));
}

void SCLabData::ExecuteNew() {
	try {
		// Backup state if required
		if (this->engine.GetMaxNbMemorisedStates() > 0) this->memorise_lab_state();
		// Indicates that it's a new iteration
		this->last_is_log = false;
		// Applies a Systemic Computation
		try { this->engine.Iterate(); }
		catch (exception & e) {
			if (this->console_panel != NULL) this->console_panel->Print(wxString(e.what())+"\n");
			else cerr << e.what() << endl;
		}
		// If beginning of execution
		if ((this->engine.GetNbIterations() == 1) && (!this->execution_panel->run_togglebutton->GetValue())) {
			// Enable reset
			if (this->program_state_panel != NULL) this->program_state_panel->reset_button->Enable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RESET_TOOL, true);
			// Set execution panel
			if (this->execution_panel != NULL)
				// Disable record to file picker
				this->execution_panel->record_to_filepicker->Disable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RECORD_TO_TOOL, false);
		}
		// Disable log run and play from file picker
		if (this->playback_panel != NULL) {
			this->playback_panel->run_togglebutton->Disable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_RUN_TOOL, false);
			this->playback_panel->forward_button->Disable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_STEP_TOOL, false);
			this->playback_panel->play_from_filepicker->Disable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_LOAD_FROM_TOOL, false);
		}
		// Enable undo
		if ((this->execution_panel != NULL) && (this->program_state_panel != NULL)) {
			this->program_state_panel->undo_button->Enable(!this->execution_panel->run_togglebutton->GetValue() && (this->engine.GetNbMemorisedStates()>0));
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, this->program_state_panel->undo_button->IsEnabled());
		}
		// Record data
		if (this->rec_stream.is_open()) this->engine.WriteLastComputation(this->rec_stream);
		// Update fields
		this->update_fields();
	}
	catch(exception & e) {
		wxMessageBox(_(e.what()));
		this->NewIterationLoop(false);
	}
}

void SCLabData::ExecuteLog() {
	try {
		// Backup state if required
		if (this->engine.GetMaxNbMemorisedStates() > 0) this->memorise_lab_state();
		// Indicates that it's a log iteration
		this->last_is_log = true;
		// Read and execute last computation
		try {
			this->engine.Execute(this->log_stream);
			// If beginning of execution
			if (this->engine.GetNbIterations() == 1) {
				// Enable reset
				if (this->program_state_panel != NULL) this->program_state_panel->reset_button->Enable();
				if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RESET_TOOL, true);
				// Set execution panel
				if (this->execution_panel != NULL)
					// Disable record to file picker
					this->execution_panel->record_to_filepicker->Disable();
				if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_RECORD_TO_TOOL, false);
				// Playback panel
				if (this->playback_panel != NULL)
					// Disable play from file picker
					this->playback_panel->play_from_filepicker->Disable();
				if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_LOAD_FROM_TOOL, false);
			}
			// Record data
			if (this->rec_stream.is_open()) this->engine.WriteLastComputation(this->rec_stream);
			// Update fields
			this->update_fields();
		}
		catch (IOException & e) {
			wxMessageBox(wxString(e.Comment().c_str()));
			this->PlaybackLoop(false);
			// Output command
			if (this->console_panel != NULL) this->console_panel->Print(_("Playback finished") + wxString::Format(" (%d)\n", this->engine.GetNbComputations()));
		}
		// Enable undo
		if ((this->playback_panel != NULL) && (this->program_state_panel != NULL)) {
			this->program_state_panel->undo_button->Enable(!this->playback_panel->run_togglebutton->GetValue() && (this->engine.GetNbMemorisedStates()>0));
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, this->program_state_panel->undo_button->IsEnabled());
		}
	}
	catch(exception & e) {
		wxMessageBox(_(e.what()));
		this->PlaybackLoop(false);
	}
}

void SCLabData::ModifyUniverse(const int part_code, const std::string & value) {
	SCSystem & universe = this->engine.Universe();
	string part;
	if (part_code == 0) 	 { part = "kernel";	 universe.ReplaceKernel(value);   }
	else if (part_code == 1) { part = "schema 1"; universe.ReplaceSchema1(value); }
	else if (part_code == 2) { part = "schema 2"; universe.ReplaceSchema2(value); }
	// Output command
	if (this->console_panel != NULL)
		this->console_panel->Print(wxString::Format("Universe's %s changed to: %s\n", part.c_str(), value.c_str()));	
}

void SCLabData::SetMaxNbMemorisedStates(const unsigned int value) {
	this->engine.SetMaxNbMemorisedStates(value);
	while (this->memorised_lab_states.size() > this->engine.GetMaxNbMemorisedStates())
		this->memorised_lab_states.pop_front();
}

void SCLabData::RestorePreviousState() {
	if (this->engine.RestoreState()) {
		// Get last state
		LabState & state = this->memorised_lab_states.back();
		// If it is a log state, enable log run
		if ((state.is_log) && (this->playback_panel != NULL)) {
			this->playback_panel->run_togglebutton->Enable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_RUN_TOOL, true);
			this->playback_panel->forward_button->Enable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_PLAYBACK_STEP_TOOL, true);
		}
		// Set the last execution log value
		this->last_is_log = state.is_log;
		// Set the log pointer
		if (this->log_stream.is_open() &&
			((state.is_log) || (this->engine.GetNbIterations() == 0))) {
			this->log_stream.seekg(0);
			string str;
			for (unsigned int i=0; i<this->engine.GetNbIterations(); ++i)
				std::getline(this->log_stream, str);
		}
		// Set the visualiser state
		if (this->visualiser_panel != NULL) {
			this->visualiser_panel->SetState(state.visualiser_state);
			this->visualiser_panel->Refresh();
		}
		// Remove last state
		this->memorised_lab_states.pop_back();
		//If recording, delete last line on record
		if (this->rec_stream.is_open() && (this->execution_panel != NULL)) {
			bool ammend_error = true;
			this->rec_stream.close();
			string	rec_name = this->execution_panel->record_to_filepicker->GetPath().c_str(),
					tmp_name = rec_name + ".tmp";
			remove(tmp_name.c_str());
			if (rename(rec_name.c_str(), tmp_name.c_str()) == 0) {
				ifstream old_rec(tmp_name.c_str());
				this->rec_stream.open(rec_name.c_str());
				if (old_rec.is_open() && this->rec_stream.is_open()) {
					string line;
					for (unsigned int i=0; i<this->engine.GetNbIterations(); ++i) {
						std::getline(old_rec,line);
						this->rec_stream << line << endl;
					}
					old_rec.close();
					if (remove(tmp_name.c_str()) != 0) {
						wxMessageBox(_("Cannot delete temporary file: ")+wxString(tmp_name.c_str()));
						// Output command
						if (this->console_panel != NULL)
							this->console_panel->Print(_("Cannot delete temporary file: ") + wxString(tmp_name.c_str()) + _("\n"));
					}
					// No error
					ammend_error = false;
				}
			}
			if (ammend_error) {
				if (this->rec_stream.is_open()) this->rec_stream.close();
				wxMessageBox(_("Cannot ammend record file"));
				// Output command
				if (this->console_panel != NULL) {
					this->console_panel->Print(_("Cannot ammend record file ") + wxString(rec_name.c_str()) + _("\n"));
					this->console_panel->Print(_("Recording stops\n"));
				}
				this->execution_panel->record_to_filepicker->SetPath(_(""));
			}
		}
		// If no more memorised states, disable backward
		if (this->engine.GetNbMemorisedStates() == 0) {
			if (this->program_state_panel != NULL) this->program_state_panel->undo_button->Disable();
			if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, false);
		}
		// Update fields (without visualiser computation update)
		this->update_fields(false);
		// Output command
		if (this->console_panel != NULL)
			this->console_panel->Print(wxString::Format("Previous state restored: (%d memorised states left)\n",this->engine.GetNbMemorisedStates()) + wxString::Format(" (%d)\n", this->engine.GetNbComputations()));
	}
	else wxMessageBox(_("Cannot restore previous state"));
	// Enable/Disable step backward
	if (this->program_state_panel != NULL) {
		this->program_state_panel->undo_button->Enable(this->engine.GetNbMemorisedStates() > 0);
		if (this->tool_bar != NULL) this->tool_bar->EnableTool(ID_PROGRAM_EXECUTION_UNDO_TOOL, this->program_state_panel->undo_button->IsEnabled());
	}
}

////////////////////////////////////////////////////////////////////////////////
// Protected functions
////////////////////////////////////////////////////////////////////////////////

const bool SCLabData::execute_action() {
	if ((this->visualiser_panel == NULL) || (this->visualiser_panel->IsReady())) {
		if ((this->execution_panel != NULL) && (this->execution_panel->run_togglebutton->GetValue())) {
			if ((this->timer == 0) || ((clock() - this->last_ctime)*this->timer >= CLOCKS_PER_SEC)) {
				this->ExecuteNew();
				this->last_ctime = clock();
                if (this->execution_panel->next_computation_togglebutton->GetValue() && this->engine.GetLastComputation().has_computed)
                    this->NewIterationLoop(false, true);
			}
			return true;
		}
		else if ((this->playback_panel != NULL) && (this->playback_panel->run_togglebutton->GetValue())) {
			if ((this->timer == 0) || ((clock() - this->last_ctime)*this->timer >= CLOCKS_PER_SEC)) {
				this->ExecuteLog();
				this->last_ctime = clock();
			}
			return true;
		}
	}
	else if ((this->visualiser_panel != NULL) && (!this->visualiser_panel->IsReady()))
		return true;
	return false;
}

void SCLabData::update_fields(bool update_visualiser) {
	// Update iterations counter
	if (this->program_state_panel != NULL)
		this->program_state_panel->nbIterLabel->SetLabel(wxString::Format("%d",this->engine.GetNbIterations()));
	// Get last iteration
	const SComputationInfo & info = this->engine.GetLastComputation();
	// If a computation occurred
	if (info.has_computed) {
		// Update program state panel
		if (this->program_state_panel != NULL) {
			// Update Computations counter
			this->program_state_panel->nbCompLabel->SetLabel(wxString::Format("%d",this->engine.GetNbComputations()));
			// Update context number label
			this->program_state_panel->nbCtxLabel->SetLabel(wxString::Format("%d",this->engine.GetContexts().size()));
			// Set status bar
			if (this->status_bar != NULL) {
				this->status_bar->SetStatusText(_("contexts: ") + wxString::Format("%d", this->engine.GetContexts().size()), 3);
				this->status_bar->SetStatusText(_("computations: ") + wxString::Format("%d", this->engine.GetNbComputations()), 4);
			}
			// Update last computation fields
			wxString sys_str;
			if ((info.scope_idx >= 0) && (info.scope_idx < (int)this->program.GetSysInst().size()))
				sys_str = wxString::Format("%d:%s", info.scope_idx, this->program.GetSysDecl()[this->program.GetSysInst()[info.scope_idx].GetFirst()].GetFirst().c_str());
			else sys_str = _("");
			this->program_state_panel->scope_label->SetLabel(sys_str);
			if ((info.sys1_idx >= 0) && (info.sys1_idx < (int)this->program.GetSysInst().size()))
				sys_str = wxString::Format("%d:%s", info.sys1_idx, this->program.GetSysDecl()[this->program.GetSysInst()[info.sys1_idx].GetFirst()].GetFirst().c_str());
			else sys_str = _("");
			this->program_state_panel->sys1_label->SetLabel(sys_str);
			if ((info.ctx_idx >= 0) && (info.ctx_idx < (int)this->program.GetSysInst().size()))
				sys_str = wxString::Format("%d:%s", info.ctx_idx, this->program.GetSysDecl()[this->program.GetSysInst()[info.ctx_idx].GetFirst()].GetFirst().c_str());
			else sys_str = _("");
			this->program_state_panel->ctx_label->SetLabel(sys_str);
			if ((info.sys2_idx >= 0) && (info.sys2_idx < (int)this->program.GetSysInst().size()))
				sys_str = wxString::Format("%d:%s", info.sys2_idx, this->program.GetSysDecl()[this->program.GetSysInst()[info.sys2_idx].GetFirst()].GetFirst().c_str());
			else sys_str = _("");
			this->program_state_panel->sys2_label->SetLabel(sys_str);
		}
		// Update universe if needed
		if (this->universe_panel != NULL) {
			const SCSystem & universe = this->engine.Universe();
			const int universe_idx = (int)(&universe - &this->engine.GetSystems().front());
			if ((info.sys1_idx == universe_idx) || (info.ctx_idx == universe_idx) || (info.sys2_idx == universe_idx))
				this->universe_panel->RefreshUniverse(universe);
		}
		// Update involved systems view
		if (this->systems_panel != NULL) {
			if ((info.sys1_idx >= 0) && (info.sys1_idx < (int)this->program.GetSysInst().size()))
				this->systems_panel->Update(info.sys1_idx,	this->engine.GetSystem(info.sys1_idx));
			if ((info.ctx_idx >= 0) && (info.ctx_idx < (int)this->program.GetSysInst().size()))
				this->systems_panel->Update(info.ctx_idx,	this->engine.GetSystem(info.ctx_idx));
			if ((info.sys2_idx >= 0) && (info.sys2_idx < (int)this->program.GetSysInst().size()))
				this->systems_panel->Update(info.sys2_idx,	this->engine.GetSystem(info.sys2_idx));
		}
		// Update the visualiser if required
		if (update_visualiser && (this->visualiser_panel != NULL))
			this->visualiser_panel->DisplayComputation(info);
	}
	// Otherwise update last picked context
	else if (this->program_state_panel != NULL) {
		this->program_state_panel->scope_label->SetLabel(_(""));
		this->program_state_panel->sys1_label->SetLabel(_(""));
		wxString sys_str;
		if ((info.ctx_idx >= 0) && (info.ctx_idx < (int)this->program.GetSysInst().size()))
			sys_str = wxString::Format("%d:%s",info.ctx_idx,this->program.GetSysDecl()[this->program.GetSysInst()[info.ctx_idx].GetFirst()].GetFirst().c_str());
		else sys_str = _("");
		this->program_state_panel->ctx_label->SetLabel(sys_str);
		this->program_state_panel->sys2_label->SetLabel(_(""));
	}
}

void SCLabData::memorise_lab_state() {
	try {
		if (this->memorised_lab_states.size() >= this->engine.GetMaxNbMemorisedStates())
			this->memorised_lab_states.pop_front();
		LabState state;
		state.is_log = this->last_is_log;
		if (this->visualiser_panel != NULL) this->visualiser_panel->GetState(state.visualiser_state);
		this->memorised_lab_states.push_back(state);
	}
	catch (exception & e) {
		string s = e.what();
		s += "\nMemorised lab states potential memory allocation error: reduce the buffer size";
		throw Exception(s);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////
